package com.example.springbootwxminiappintegrat.service.impl;

import com.example.springbootwxminiappintegrat.bean.WxPayMpOrderResult;
import com.example.springbootwxminiappintegrat.bean.WxPayOrderRequest;
import com.example.springbootwxminiappintegrat.bean.WxPayOrderResult;
import com.example.springbootwxminiappintegrat.service.MiniAppWxPayService;
import com.google.gson.Gson;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.SignatureException;
import java.util.Base64;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

/**
 * @author qurenneng
 */
@Slf4j
@Service
public class MiniAppWxPayServiceImpl implements MiniAppWxPayService {


    private final HttpClient httpClient;
    private final PrivateKey privateKey;
    @Value("${appid}")
    private final String appid;
    @Value("${mchid}")
    private final String mchid;
    private final static String PAYUNIFIEDORDERURL = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";

    public MiniAppWxPayServiceImpl(HttpClient httpClient, PrivateKey privateKey, String appid, String mchid) {
        this.httpClient = httpClient;
        this.privateKey = privateKey;
        this.appid = appid;
        this.mchid = mchid;
    }

    /**
     * JSAPI 下单接口 接口文档: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml
     * 接口说明:商户系统先调用该接口在微信支付服务后台生成预支付交易单，返回正确的预支付交易会话标识后再按Native、JSAPI、APP等不同场景生成交易串调起支付。
     */
    @Override
    public String payTransactionsJsapi(WxPayOrderRequest wxPayOrderRequest) throws IOException {
        wxPayOrderRequest.setAppid(appid);
        wxPayOrderRequest.setMchid(mchid);
        WxPayOrderResult wxPayUnifiedOrderResult = this.unifiedOrder(wxPayOrderRequest);
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        WxPayMpOrderResult wxPayMpOrderResult = new WxPayMpOrderResult().setAppId(
                        wxPayOrderRequest.getAppid())
                .setNonceStr(wxPayOrderRequest.getOut_trade_no())
                .setPackageValue("prepay_id=" + wxPayUnifiedOrderResult.getPrepay_id())
                .setSignType("RSA")
                .setTimeStamp(timestamp);
        /**
         * jsapi 拉起支付文档 : https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_4.shtml
         * 一定要注意签名方式哪里
         */
        String paySign = sign(getSignStr(wxPayOrderRequest.getAppid(), timestamp,
                        wxPayOrderRequest.getOut_trade_no(), "prepay_id=" + wxPayUnifiedOrderResult.getPrepay_id())
                , privateKey);
        wxPayMpOrderResult.setPaySign(paySign);
        /**
         * 这里是为了做兼容 代码不允许有 package 的处理，所以才做转换，如果可以的话，让前端去做处理也行
         */
        Gson gson = new Gson();
        Map<String, Object> map = gson.fromJson(gson.toJson(wxPayMpOrderResult), Map.class);
        map.put("package", map.get("packageValue"));
        map.remove("packageValue");
        return gson.toJson(map);
    }


    private String getSignStr(String appId, String timeStamp, String nonceStr, String packageValue) {
        return String.format("%s\n%s\n%s\n%s\n", appId, timeStamp, nonceStr, packageValue);
    }

    public static String sign(String string, PrivateKey privateKey) {
        try {
            Signature sign = Signature.getInstance("SHA256withRSA");
            sign.initSign(privateKey);
            sign.update(string.getBytes());
            return Base64.getEncoder().encodeToString(sign.sign());
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持SHA256withRSA", e);
        } catch (SignatureException e) {
            throw new RuntimeException("签名计算失败", e);
        } catch (InvalidKeyException e) {
            throw new RuntimeException("无效的私钥", e);
        }
    }


    /**
     * 请求统一下单逻辑处理:
     */
    private WxPayOrderResult unifiedOrder(WxPayOrderRequest wxPayOrderRequest) throws IOException {
        HttpPost httpPost = new HttpPost(PAYUNIFIEDORDERURL);
        // 请求body参数
        StringEntity entity = new StringEntity(new Gson().toJson(wxPayOrderRequest), "UTF-8");
        entity.setContentType("application/json;charset=UTF-8");
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json;charset=UTF-8");
        HttpResponse response = httpClient.execute(httpPost);
        int code = response.getStatusLine().getStatusCode();
        String body = response.getEntity() == null ? null : EntityUtils.toString(response.getEntity());
        if (code != 200) {
            log.error("请求微信统一下单异常: code={}, body={}", code, body);
            throw new RuntimeException("请求微信统一下单异常");
        }
        return new Gson().fromJson(body,
                WxPayOrderResult.class);
    }
}
